#include "StdAfx.h"

Mutex m_mpqMutex;

CTextureHandler* Singleton<CTextureHandler>::m_instance = 0;

Texture* CTextureHandler::GetTexture(std::string szTexture)
{
	//Log::Print("%s(%u): Loading texture '%s'...", __FILE__, __LINE__, szTexture.c_str());
	std::transform(szTexture.begin(), szTexture.end(), szTexture.begin(), tolower);
	m_textureMutex.Acquire();
	if(m_textures[szTexture])
	{
		//Log::Print("... found in cache! (thats fast :P)\n");
		m_textures[szTexture]->m_refCount++;
		m_textureMutex.Release();
		return m_textures[szTexture];
	}
	CMpqFile f(szTexture);
	if(!f.IsOpened())
	{
		//Log::Print("... not found in MPQ! :(\n");
		m_textureMutex.Release();
		return 0;
	}
	char sig[4];
	int w, h, version;
	ui32 offsets[16], sizes[16];
	ui8 compression, alphadepth, alphaencoding, hasMipMap;

	f.ReadInc(4, sig);
	f.ReadInc(4, &version);
	f.ReadInc(1, &compression);
	f.ReadInc(1, &alphadepth);
	f.ReadInc(1, &alphaencoding);
	f.ReadInc(1, &hasMipMap);
	f.ReadInc(4, &w);
	f.ReadInc(4, &h);
	f.ReadInc(64, offsets);
	f.ReadInc(64, sizes);
	D3DFORMAT format = (D3DFORMAT)0;
	ui32 blocksize = 0;
	if(version == 1)
	{
		if(compression == 2) // DXT3 or DXT5
		{
			switch(alphaencoding)
			{
			case 0: // DXT1
				format = D3DFMT_DXT1;
				blocksize = 2;
				break;
			case 1: // DXT2/3
				format = D3DFMT_DXT3;
				blocksize = 4;
				break;
			case 7: // DXT4/5
				format = D3DFMT_DXT5;
				blocksize = 4;
				break;
			}
		}
		else if(compression == 1)
		{
			ui32 palette[256];
			f.ReadInc(256 * 4, palette);
			ui32 levelcount = 0;
			for(ui32 i = 0; i < 16; ++i)
				if(offsets[i] && sizes[i])
					++levelcount;
			IDirect3DTexture9* tex;
			sD3D->CreateTexture(w, h, levelcount, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, 0);
			PROCESS_MEMORY_COUNTERS pmc;
			GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
			ui32 usedBefore = pmc.WorkingSetSize;
			for(ui32 i = 0, a = 0; i < 16 && a < levelcount; ++i)
			{
				if(offsets[i] && sizes[i])
				{
					IDirect3DSurface9* surf;
					tex->GetSurfaceLevel(a, &surf);
					D3DSURFACE_DESC desc;
					surf->GetDesc(&desc);
					ui8* paletteIndices = new ui8[desc.Width * desc.Height];
					f.SetInc(offsets[i]);
					f.ReadInc(desc.Width * desc.Height, paletteIndices);
					ui32* colors = new ui32[desc.Width * desc.Height];
					for(ui32 j = 0; j < desc.Width * desc.Height; ++j)
					{
						colors[j] = palette[paletteIndices[j]];
					}
					ui8* alpha = new ui8[desc.Width * desc.Height];
					f.ReadInc(desc.Width * desc.Height, alpha);
					for(ui32 j = 0; j < desc.Width * desc.Height; ++j)
					{
						colors[j] &= ~ui32(0xFF000000);
						colors[j] |= (ui32(alpha[j]) << 24);
					}
					RECT r;
					r.left = r.top = 0;
					r.right = desc.Width;
					r.bottom = desc.Height;
					D3DXLoadSurfaceFromMemory(surf, 0, 0, colors, D3DFMT_A8R8G8B8, 4 * r.right, 0, &r, D3DX_DEFAULT, 0);
					delete [] colors;
					delete [] paletteIndices;
					delete [] alpha;
					++a;
				}
			}

			Texture* pTexture = new Texture(tex);
			m_textures[szTexture] = pTexture;
			m_textureMutex.Release();
			pTexture->m_isADTTexture = false;
			return pTexture;
		}
	}
	ui32 levelcount = 0;
	for(ui32 i = 0; i < 16; ++i)
		if(offsets[i] && sizes[i])
			++levelcount;

	IDirect3DTexture9* tex;

	HRESULT hRes = sD3D->CreateTexture(w, h, levelcount, 0, format, D3DPOOL_MANAGED, &tex, 0);
	if(FAILED(hRes))
	{
		//Log::Print("... IDirect3DDevice9::CreateTexture failed with code '%u' (Parameters: Levelcount: %u  Width: %u  Height: %u  Format: %u)! :(\n", HRESULT_CODE(hRes), levelcount, w, h, format);
		m_textureMutex.Release();
		return 0;
	}

	try
	{
		for(ui32 i = 0, a = 0; i < 16 && a < levelcount; ++i)
		{
			if(offsets[i] && sizes[i])
			{
				ui8* data = new ui8[sizes[i]];
				f.Read(offsets[i], sizes[i], data);
				IDirect3DSurface9* surf;
				tex->GetSurfaceLevel(a, &surf);
				D3DSURFACE_DESC desc;
				surf->GetDesc(&desc);
				RECT r;
				r.left = r.top = 0;
				r.right = desc.Width;
				r.bottom = desc.Height;
				try
				{
					if(D3DXLoadSurfaceFromMemory(surf, 0, 0, data, format, blocksize * r.right, 0, &r, D3DX_DEFAULT, 0) != D3D_OK)
					{
						MessageBox(0, "Load from Memory", "Failed!", MB_OK);
						// return 0; // TODO: Figure out if we should cancle here or just try loading the next mipmap...
					}
					delete [] data;
				}
				catch(...)
				{
					delete [] data;
					return 0;
				}
				++a;
			}
		}
	}
	catch(...)
	{
		return 0;
	}

	
	//Log::Print("... loaded and ready!\n");

	Texture* pTexture = new Texture(tex);
	pTexture->szTexName = szTexture;
	m_textures[szTexture] = pTexture;
	m_textureMutex.Release();
	pTexture->m_isADTTexture = false;
	return pTexture;
}

void CTextureHandler::Remove(Texture* tex)
{
	m_textures[tex->szTexName] = 0;
}